﻿// © Benn Eichhorn 2010
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Algobox.Feeds.Finance.Bats.PitchClient.Messages
{
    /// <summary>
    /// Singleton Bats Trade Message
    /// </summary>
    public class Trade : IMessage
    {
        private Trade() { }

        private static Trade _instance = new Trade();

        /// <summary>
        /// Get's the singleton instance of the OrderAdd class
        /// </summary>
        /// <returns>Current OrderAdd</returns>
        public static Trade GetInstance() { return _instance; }
        
        public char MessageType { get { return 'P'; } }

        /// <summary>
        /// Milliseconds since midnight
        /// </summary>
        public int TimeStampMillis;

        /// <summary>
        /// Slow, calculates current TimeStamp from TimeStampMillis
        /// </summary>
        public DateTime TimeStamp { get { return BatsMessageParser.Date.AddMilliseconds(TimeStampMillis); } }

        /// <summary>
        /// Day unique order reference
        /// </summary>
        public string OrderReference;

        /// <summary>
        /// Day unique trade reference
        /// </summary>
        public string TradeReference;

        /// <summary>
        /// True for buy orders, false for sell orders
        /// </summary>
        public bool IsBuy;
                
        /// <summary>
        /// Total number of shares being added to the book (may be less than the number of shares entered because part of the order may trade before being posted to the book)
        /// </summary>
        public ulong Shares;

        /// <summary>
        /// Stock symbol right padded with spaces
        /// </summary>
        public string Symbol;

        /// <summary>
        /// price as it comes from exchange, WARNING this can be in long (7) or short (4) format
        /// </summary>
        private long PriceRaw;

        /// <summary>
        /// The display price of the order
        /// </summary>
        public double Price;


        public override string ToString()
        {
            return String.Format("IMessage->OrderAdd: Stock={0},IsBuy={1},Shares={2},Price={3},OrderReference={4},TimeStamp={5}", Symbol, IsBuy, Shares, Price, OrderReference, TimeStamp);
        }

        // Order Add
        const int S_P_ORDERREF_LOC = 10;
        const int S_P_ORDERREF_LEN = 12;
        const int S_P_BUYSELL_LOC = 22;
        const int S_P_SHARES_LOC = 23;
        const int S_P_SHARES_LEN = 6;
        const int S_P_STOCK_LOC = 29;
        const int S_P_STOCK_LEN = 6;
        const int S_P_PRICE_LOC = 35;
        const int S_P_PRICE_LEN = 10;
        const int S_P_TRADEREF_LOC = 45;
        const int S_P_TRADEREF_LEN = 12;
        const int S_P_PRICE_DECIMAL_DIVISOR = 10000;

        // Order Add Long
        const int S_p_ORDERREF_LOC = 10;
        const int S_p_ORDERREF_LEN = 12;
        const int S_p_BUYSELL_LOC = 22;
        const int S_p_SHARES_LOC = 23;
        const int S_p_SHARES_LEN = 10;
        const int S_p_STOCK_LOC = 33;
        const int S_p_STOCK_LEN = 6;
        const int S_p_PRICE_LOC = 39;
        const int S_p_PRICE_LEN = 19;
        const int S_p_TRADEREF_LOC = 58;
        const int S_p_TRADEREF_LEN = 12;
        const int S_p_PRICE_DECIMAL_DIVISOR = 10000000;
        

        internal static IMessage ParseShort(ref string inbound)
        {
            // get sequenced data message timestamp
            if (!Int32.TryParse(inbound.Substring(BatsMessageParser.S_TIMESTAMP_LOC, BatsMessageParser.S_TIMESTAMP_LEN), out _instance.TimeStampMillis))
            {
                Messages.Error message = Messages.Error.GetInstance("Sequenced data message failed to parse TimeStamp millis as Double", ref inbound);
                return message;
            }
            if (!UInt64.TryParse(inbound.Substring(S_P_SHARES_LOC, S_P_SHARES_LEN), out _instance.Shares))
            {
                Messages.Error message = Messages.Error.GetInstance("OrderAdd failed to parse Shares as Int64", ref inbound);
                return message;
            }
            if (!Int64.TryParse(inbound.Substring(S_P_PRICE_LOC, S_P_PRICE_LEN), out _instance.PriceRaw))
            {
                Messages.Error message = Messages.Error.GetInstance("OrderAdd failed to parse Price as Int64", ref inbound);
                return message;
            }
            
            _instance.OrderReference = inbound.Substring(S_P_ORDERREF_LOC, S_P_ORDERREF_LEN);
            _instance.TradeReference = inbound.Substring(S_P_TRADEREF_LOC, S_P_TRADEREF_LEN);

            _instance.IsBuy = inbound[S_P_BUYSELL_LOC] == 'B' ? true : false;

            _instance.Symbol = inbound.Substring(S_P_STOCK_LOC, S_P_STOCK_LEN).TrimEnd();
            _instance.Price = (double)_instance.PriceRaw / S_P_PRICE_DECIMAL_DIVISOR;

            return _instance;
        }

        internal static IMessage ParseLong(ref string inbound)
        {
            // get sequenced data message timestamp
            if (!Int32.TryParse(inbound.Substring(BatsMessageParser.S_TIMESTAMP_LOC, BatsMessageParser.S_TIMESTAMP_LEN), out _instance.TimeStampMillis))
            {
                Messages.Error message = Messages.Error.GetInstance("Sequenced data message failed to parse TimeStamp millis as Double", ref inbound);
                return message;
            }

            if (!UInt64.TryParse(inbound.Substring(S_p_SHARES_LOC, S_p_SHARES_LEN), out _instance.Shares))
            {
                Messages.Error message = Messages.Error.GetInstance("OrderAdd failed to parse Shares as Int64", ref inbound);
                return message;
            }
            if (!Int64.TryParse(inbound.Substring(S_p_PRICE_LOC, S_p_PRICE_LEN), out _instance.PriceRaw))
            {
                Messages.Error message = Messages.Error.GetInstance("OrderAdd failed to parse Price as Int64", ref inbound);
                return message;
            }

            _instance.OrderReference = inbound.Substring(S_p_ORDERREF_LOC, S_p_ORDERREF_LEN);
            _instance.TradeReference = inbound.Substring(S_p_TRADEREF_LOC, S_p_TRADEREF_LEN);

            _instance.IsBuy = inbound[S_p_BUYSELL_LOC] == 'B' ? true : false;

            _instance.Symbol = inbound.Substring(S_p_STOCK_LOC, S_p_STOCK_LEN).TrimEnd();
            _instance.Price = (double)_instance.PriceRaw / S_p_PRICE_DECIMAL_DIVISOR;

            return _instance;
        }

        /// <summary>
        /// Deep clone
        /// </summary>
        /// <returns></returns>
        public Trade Clone()
        {
            Trade clone = new Trade();
            clone.IsBuy = _instance.IsBuy;
            clone.OrderReference = _instance.OrderReference;
            clone.TradeReference = _instance.TradeReference;
            clone.TimeStampMillis = _instance.TimeStampMillis;
            clone.Price = _instance.Price;
            clone.PriceRaw = _instance.PriceRaw;
            clone.Shares = _instance.Shares;
            clone.Symbol = _instance.Symbol;
            return clone;
        }
    }
}
